/*
 * proc.c
 *
 *  Created on: 2016-11-3
 *      Author: m
 */
#include "proc.h"
#include "global.h"
#include "string.h"
#include "protect.h"
#include "syscall.h"
#include "debug.h"

static int s_new_gdt_idx = INDEX_LDT_FIRST;

void shedule() {
    int tick_max = 0;

    while(0 == tick_max) {
        for(int i = 0; i < NR_TASKS; ++i) {
            if ((tick_max < g_proc_table[i].ticks) && (PROC_ST_READY == g_proc_table[i].state)) {
                tick_max = g_proc_table[i].ticks;
                g_proc_ready = &g_proc_table[i];
            }
        }
        if(0 == tick_max) {
            for(int i = 0; i < NR_TASKS; ++i) {
                if(PROC_ST_READY == g_proc_table[i].state) {
                    g_proc_table[i].ticks = g_proc_table[i].priority;
                }
            }
        }
    }
}

int init_proc(PROCESS* p, TASK* t) {
    p->state = PROC_ST_READY;
    p->priority = t->priority;
    p->ticks = t->priority;

    strcpy(p->name, t->name);
    p->pid = p - g_proc_table;
    p->tty_no = 0;

    p->ldt_sel = s_new_gdt_idx * 8;
    //add ldt-item in gdt-tab
    init_descriptor(&g_gdt[s_new_gdt_idx], p->ldts, LDT_ITEM_COUNT * sizeof(DESCRIPTOR) - 1, DA_LDT);
    s_new_gdt_idx++;

    memcpy(&p->ldts[0], &g_gdt[SELECTOR_KERNEL_CS >> 3], sizeof(DESCRIPTOR));
    p->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5; //非一致 可执行 DPL=1
    memcpy(&p->ldts[1], &g_gdt[SELECTOR_KERNEL_DS >> 3], sizeof(DESCRIPTOR));
    p->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5; //存在的可读写数据段 DPL=1

    p->regs.gs = 0x18 | RPL_TASK;
    p->regs.fs = 8 | SA_TIL | RPL_TASK;
    p->regs.es = 8 | SA_TIL | RPL_TASK;
    p->regs.ds = 8 | SA_TIL | RPL_TASK;

    p->regs.cs = 0 | SA_TIL | RPL_TASK;
    p->regs.ss = 8 | SA_TIL | RPL_TASK;

    p->regs.eip= (unsigned int)t->initial_eip;
    p->regs.esp= t->stack_top;
    p->regs.eflags = 0x1202;   // IF=1, IOPL=1, bit 2 is always 1. 0x1202恰好设置了IF位并把IOPL设为1。这样,进程就可以使用I/O指令,并且中断会在iretd执行时被打开

    return 0;
}

static void block_on(PROCESS* p, int st) {
    p->state |= st;
    shedule();
}

static void unblock_on(PROCESS* p, int remove_state) {
    p->state &= ~remove_state;
    shedule();
}

//ret 0:hit 1:not hit
static int do_check_int_msg(PROCESS* who_want_recv, int from_which, MESSAGE* msg) {
    if(who_want_recv->ipc.has_int_msg && (TASK_ANY == from_which || INTERRUPT == from_which)) {
        MESSAGE tmsg = {0};
        tmsg.type = MT_INTERRUPT;
        tmsg.req_pid = INTERRUPT;
        memcpy(msg, &tmsg, sizeof(MESSAGE));

        who_want_recv->ipc.has_int_msg = 0;
        return 0;
    }
    return -1;
}

static int msg_recv(PROCESS* who_want_recv, int from_which_pid, MESSAGE* msg) {
    PROCESS* prev = 0;
    PROCESS* from = 0;
    int msg_exist = 0;

    if(0 == do_check_int_msg(who_want_recv, from_which_pid, msg)) {
        return 0;
    }

    if(TASK_ANY == from_which_pid) {
        if(who_want_recv->ipc.q_sending) {
            msg_exist = 1;
            from = who_want_recv->ipc.q_sending;
        }
    } else {
        from = &g_proc_table[from_which_pid];
        if((PROC_ST_SENDING & from->state) && (who_want_recv->pid == from->ipc.send_to)) {
            msg_exist = 1;
            PROCESS* p = who_want_recv->ipc.q_sending;
            while(p) {
                if(p->pid == from_which_pid) {
                    from = p;
                    break;
                }
                prev = p;
                p = p->ipc.next_sending;
            }
        }
    }

    if(msg_exist) {
        if(from == who_want_recv->ipc.q_sending) { // 1st element
            who_want_recv->ipc.q_sending = from->ipc.next_sending;
            from->ipc.next_sending = 0;
        } else {
            prev->ipc.next_sending = from->ipc.next_sending;
            from->ipc.next_sending = 0;
        }
        assert(msg);
        assert(from->ipc.msg);
        memcpy(msg, from->ipc.msg, sizeof(MESSAGE));
        from->ipc.send_to = TASK_NONE;
        from->ipc.msg = 0;

        unblock_on(from, PROC_ST_SENDING);
    } else {
        who_want_recv->ipc.msg = msg;
        who_want_recv->ipc.recv_from = from_which_pid;
        block_on(who_want_recv, PROC_ST_RECEIVING);
    }
    return 0;
}

//ret 0:no dead block, other: dead block
static int dead_block(PROCESS* sender, PROCESS* receiver) {
    return 0;
}
static int msg_send(PROCESS* who_want_send, int to_which_pid, MESSAGE* msg) {
    PROCESS* proc_dest = &g_proc_table[to_which_pid];

    if(dead_block(who_want_send, proc_dest)) {
        assert_a(0, NG_ACT_HALT);
        return -1;
    }

    if ((proc_dest->state & PROC_ST_RECEIVING)
            && (TASK_ANY == proc_dest->ipc.recv_from || who_want_send->pid == proc_dest->ipc.recv_from)) {
        //这块可能有bug：proc_dest->com.recv_from是TASK_ANY的情况下，并且proc_dest->state是PROC_ST_RECEIVING，但proc_dest有可能有向其发送等待的发送队列
        assert(proc_dest->ipc.msg);
        assert(msg);
        memcpy(proc_dest->ipc.msg, msg, sizeof(MESSAGE));
        proc_dest->ipc.recv_from = TASK_NONE;
        proc_dest->ipc.msg = 0;
        unblock_on(proc_dest, PROC_ST_RECEIVING);
    } else {
        who_want_send->ipc.send_to = to_which_pid;
        who_want_send->ipc.msg = msg;

        PROCESS* p = proc_dest->ipc.q_sending;
        if(p) {
            while(p->ipc.next_sending) {
                p = p->ipc.next_sending;
            }
            p->ipc.next_sending = who_want_send;
        } else {
            proc_dest->ipc.q_sending = who_want_send;
        }
        who_want_send->ipc.next_sending = 0;
        block_on(who_want_send, PROC_ST_SENDING);
    }
    return 0;
}
char* pid2name(int pid) {
    if(pid < NR_TASKS && pid >= 0) {
        return g_proc_table[pid].name;
    } else if(TASK_ANY == pid) {
        return "ANY";
    } else if(TASK_NONE == pid) {
        return "NONE";
    }
    return "Err";
}
int post_impl(int func, int dest_pid, MESSAGE* msg, PROCESS* req) {
    msg->req_pid = req->pid;
    if(FN_SEND == func) {
        //printf("[send] %s->%s\n", req->name, pid2name(dest_pid));
        return msg_send(req, dest_pid, msg);
    } else if(FN_RECV == func) {
        //printf("[recv] %s->%s\n", req->name, pid2name(dest_pid));
        return msg_recv(req, dest_pid, msg);
    } else {
        //err
    }
    return -1;
}

int post(int func, int dest_pid, MESSAGE* msg) {
    if(FN_BOTH == func) {
        if(syscall_post(FN_SEND, dest_pid, msg)) {
            return -1;
        }
        if(syscall_post(FN_RECV, dest_pid, msg)) {
            return -1;
        }
    } else if(FN_SEND == func) {
        if(syscall_post(FN_SEND, dest_pid, msg)) {
            return -1;
        }
    } else if(FN_RECV == func) {
        memset(msg, 0, sizeof(MESSAGE));
        if(syscall_post(FN_RECV, dest_pid, msg)) {
            return -1;
        }
    } else {
        return -1;
    }
    return 0;
}

void notify_int(int pid) {
    if(pid >= 0 && pid < NR_TASKS) {
        PROCESS* p = &g_proc_table[pid];
        if(p->state & PROC_ST_RECEIVING &&
                (TASK_ANY == p->ipc.recv_from || INTERRUPT == p->ipc.recv_from)) {//target task is waiting for this interrupt
            assert(p->ipc.msg);
            p->ipc.msg->type = MT_INTERRUPT;
            p->ipc.msg->req_pid = INTERRUPT;
            p->ipc.msg = 0;
            p->ipc.recv_from = TASK_NONE;
            p->ipc.has_int_msg = 0;
            unblock_on(p, PROC_ST_RECEIVING);
        } else {
            p->ipc.has_int_msg = 1;
        }
    } else {
        assert(0);
    }
}
